import {
  defineComponent,
  ref,
  onMounted,
  onBeforeUnmount,
  watch,
  type VNode,
  type CSSProperties
} from "vue";
import { virtualTableProps } from "./props";
import { useColumnManager } from "./useColumnManager";
import { useRowStore } from "./useRowStore";
import { useResizeObserver } from "./useResizeObserver";
import { useTreeRows } from "./useTreeRows";
import type { RowData } from "./types";
export default defineComponent({
  name: "VirtualTable",
  props: virtualTableProps,
  setup(props, { expose, attrs }) {
    const containerRef = ref<HTMLElement | null>(null);

    const { flatRows, collapse, expand, toggle, isCollapsed } = useTreeRows(
      props.data as RowData[],
      props.defaultExpandedKeys
    );

    const rowStore = useRowStore({
      estimated: props.estimatedRowHeight,
      total: flatRows.value.length,
      overscan: props.overscanRowCount,
    });

    watch(
      () => flatRows.value.length,
      (len) => rowStore.reset(len)
    );

    const { state: col, recalculate } = useColumnManager(props.columns, 0);

    useResizeObserver(
      () => containerRef.value,
      () => {
        recalculate(containerRef.value!.clientWidth);
      }
    );

    const widthUpd = () => {
      const trs =
        containerRef.value!.querySelectorAll<HTMLElement>("tr[data-row]");
      trs.forEach((tr) => {
        const i = Number(tr.dataset.row);
        rowStore.setRowHeight(i, tr.offsetHeight);
      });
    };
    window.addEventListener("vt-column-width-updated", widthUpd);
    onBeforeUnmount(() =>
      window.removeEventListener("vt-column-width-updated", widthUpd)
    );

    const observeRow = (el: HTMLElement, i: number) => {
      if (!props.variableRowHeight) return;
      let last = 0;
      const ro = new ResizeObserver(() => {
        const h = el.offsetHeight;
        if (h !== last) {
          last = h;
          rowStore.setRowHeight(i, h);
        }
      });
      ro.observe(el);
      onBeforeUnmount(() => ro.disconnect());
    };

    const onScroll = () => {
      const top = containerRef.value!.scrollTop;
      const vpH = containerRef.value!.clientHeight;
      rowStore.updateVisible(top, vpH);
    };

    onMounted(() => {
      recalculate(containerRef.value!.clientWidth);
      onScroll();
    });

    const renderHeader = () => (
      <table
        class="vt-table"
        style={{
          width: "100%",
          borderCollapse: "collapse",
          position: "sticky",
          top: 0,
          zIndex: 10,
        }}
      >
        <thead>
          <tr>
            {props.columns.map((c, i) => {
              const style: CSSProperties = {
                width: col.widths[i] + "px",
                position: c.fixed ? "sticky" : undefined,
                left:
                  c.fixed === "left"
                    ? col.leftOffsets[c.key] + "px"
                    : undefined,
                right:
                  c.fixed === "right"
                    ? col.rightOffsets[c.key] + "px"
                    : undefined,
              };
              return (
                <th key={c.key} class="vt-header-cell" style={style}>
                  {c.title}
                </th>
              );
            })}
          </tr>
        </thead>
      </table>
    );

    const renderBody = () => {
      const totalH =
        rowStore.state.prefix.at(-1)! + rowStore.state.heights.at(-1)!;
      const rows: VNode[] = [];

      for (let i = rowStore.state.start; i <= rowStore.state.end; i++) {
        const { row, depth } = flatRows.value[i];
        const rowStyle: CSSProperties = {
          transform: `translateY(${rowStore.state.prefix[i]}px)`,
          height: rowStore.state.heights[i] + "px",
        };

        rows.push(
          <tr
            key={row.id}
            data-row={i}
            class="vt-row"
            style={rowStyle}
            ref={(el) => el && observeRow(el as HTMLElement, i)}
          >
            {props.columns.map((c, ci) => {
              const cellStyle: CSSProperties = {
                width: col.widths[ci] + "px",
                position: c.fixed ? "sticky" : undefined,
                left:
                  c.fixed === "left"
                    ? col.leftOffsets[c.key] + "px"
                    : undefined,
                right:
                  c.fixed === "right"
                    ? col.rightOffsets[c.key] + "px"
                    : undefined,
                /* 左缩进：仅对第一列展示树层级缩进 */
                paddingLeft: ci === 0 ? depth * 16 + "px" : undefined,
              };
              return (
                <td key={c.key} class="vt-cell" style={cellStyle}>
                  {row[c.key] as any}
                </td>
              );
            })}
          </tr>
        );
      }

      return (
        <div style={{ height: totalH + "px", position: "relative" }}>
          <table
            class="vt-table"
            style={{
              position: "absolute",
              top: 0,
              left: 0,
              borderCollapse: "collapse",
            }}
          >
            <tbody>{rows}</tbody>
          </table>
        </div>
      );
    };

    /* ---------- public API ---------- */
    function scrollToRow(i: number) {
      containerRef.value!.scrollTop = rowStore.state.prefix[i] ?? 0;
    }
    function forceUpdate() {
      recalculate(containerRef.value!.clientWidth);
      rowStore.forceRemeasure();
    }

    expose({
      scrollToRow,
      forceUpdate,
      collapse,
      expand,
      toggle,
      isCollapsed,
    });

    return () => (
      <div
        ref={containerRef}
        class={`vt-container ${attrs.class ?? ""}`}
        style={{
          overflow: "auto",
          width: "100%",
          height: "100%",
          ...(attrs.style as CSSProperties),
        }}
        onScroll={onScroll}
      >
        {renderHeader()}
        {renderBody()}
      </div>
    );
  },
});
